var __celljs__, $exec, $cell, $path, $async, $defer, $await;
(function (){
    var IE = !!window.ActiveXObject;
    var IE678 = IE && !window.addEventListener;

    function assert(expr, msg){
        if (!expr) {
            throw msg;
        }
    }

    var _hop = Object.prototype.hasOwnProperty;
    function hop(obj, key){
        return _hop.call(obj, key);
    }

    function mix(dest, src){
        for (var k in src) {
            if (hop(src, k)) {
                dest[k] = src[k];
            }
        }
        return dest;
    }

    function makeObject(array){
        var ret = {};
        for (var j=array.length; j--;) {
            ret[array[j-1]] = array[j--];
        }
        return ret;
    }

    function lastOne(array){
        return array[array.length - 1];
    }

    function sortBy(array, evaluator, desc){
        return array.sort(function (a, b){
            a = evaluator(a);
            b = evaluator(b);
            a = a === b ? 0 : a < b ? -1 : 1;
            return desc ? -a : a;
        });
    }

    function leftOfLast(str, part){
        var index = str.lastIndexOf(part);
        return index !== -1 ? str.slice(0, index) : '';
    }

    function isDOMReady(doc){
        if (!IE678) {
            if (IE) {
                return doc.readyState === 'loaded' || doc.readyState === 'complete';
            } else {
                return doc.readyState !== 'loading';
            }
        }
        try {
            doc.createElement('div').doScroll('left');
            return true;
        } catch(ex) {}
        return false;
    }

    function isBeforeReady(){
        return !isDOMReady(document);
    }

    function getScripts(){
        return document.getElementsByTagName('script');
    }

    function getLastScript(){
        return lastOne(getScripts());
    }

    function removeNode(node){
        node.parentNode.removeChild(node);
    }

    var _interactive_script;
    function getInteractiveScript(){
        if (!IE) {
            return getLastScript();
        }
        var script = _interactive_script;
        if (script && script.readyState === 'interactive') {
            return script;
        }
        var scripts = getScripts();
        for (var j=scripts.length; j--;) {
            script = scripts[j];
            if (script.readyState === 'interactive') {
                break;
            }
        }
        return (_interactive_script = script);
    }

    var _commentRegex = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
    var _$cellRegex = /\$cell\(['"]([^'"]+)['"]\)/g;
    function parseDeps(def){
        var code = (def + '').replace(_commentRegex, '');
        var match;
        var ret = [];
        _$cellRegex.lastIndex = 0;
        while (match = _$cellRegex.exec(code)) {
            if (match[1]) {
                ret.push(match[1]);
            }
        }
        return ret;
    }

    function Cell(path){
        this.path = path;
        this.deps = [];
        this.reverse_deps = [];
    }
    Cell.prototype.def = null;
    Cell.prototype.exports = undefined;
    Cell.prototype.defined = false;
    Cell.prototype.requested = false;
    Cell.prototype.by = null;
    Cell.prototype._undefined_deps_count = 0;

    var registry = {};
    function getCell(path){
        var cell = registry[path];
        if (!cell) {
            cell = new Cell(path);
            registry[path] = cell;
        }
        return cell;
    }

    function onCellDepDefined(cell){
        cell._undefined_deps_count --;
        checkDefineCell(cell);
    }

    function defineCell(cell){
        cell.exports = (cell.def)(require);
        cell.defined = true;
        var reversed_deps = cell.reverse_deps;
        for (var j=reversed_deps.length; j--;) {
            onCellDepDefined(reversed_deps[j]);
        }
    }

    function checkDefineCell(cell){
        assert(cell._undefined_deps_count >= 0);
        if (!cell._undefined_deps_count) {
            if (!cell.defined) {
                defineCell(cell);
            }
        }
    }

    function loadScript(url, attributes){
        if (isBeforeReady()) {
            var buff = ['<script type="text/javascript" charset="utf-8" src="', url];
            buff.push('"' +
                ' onload="__celljs__._scriptLoad.call(this);"' +
                ' onreadystatechange="__celljs__._scriptRSChange.call(this);"' +
                ' onerror="__celljs__._scriptError.call(this);"');
            for (var i=0; i<attributes.length; i+=2) {
                buff.push(' ');
                buff.push(attributes[i]);
                buff.push('="');
                buff.push(attributes[i+1]);
                buff.push('"');
            }
            buff.push('></script>');
            document.write(buff.join(''));
            return getLastScript();
        }
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.charset = 'utf-8';
        script.src = url;
        for (var j=0; j<attributes.length; j+=2) {
            script.setAttribute(attributes[j], attributes[j+1]);
        }
        script.onload = onScriptLoad;
        script.onreadystatechange = onScriptReadyStateChange;
        script.onerror = onScriptError;
        document.body.appendChild(script);
        return script;
    }

    function cleanUpScript(script){
        script.onload = null;
        script.onreadystatechange = null;
        script.onerror = null;
        removeNode(script);
    }

    function onScriptLoad(){
        var script = this;
        var cell = getCell(script.getAttribute('data-path'));
        if (!(IE || isBeforeReady())) {
            assert(_waiting_def);
            cellGotDef(cell, _waiting_def);
            _waiting_def = null;
        }
        assert(cell.def, ['cell "', cell.path, '" has no def!'].join(''));
        cleanUpScript(script);
    }

    function onScriptError(){
        var script = this;
        var cell = getCell(script.getAttribute('data-path'));
        assert(false, ['cell "', cell.path, '" is not exist!'].join(''));
        cleanUpScript(script);
    }

    function onScriptReadyStateChange(){
        var script = this;
        if (script.readyState === 'complete' ||
            script.readyState === 'loaded') {
            if (IE678) {
                setTimeout(function (){
                    onScriptLoad.call(script);
                }, 0);
            }
        }
    }

    function requestCell(cell, by){
        var fullPath = resolve(cell.path);
        var script = loadScript(fullPath, [
            "data-path", cell.path
        ]);
        cell.requested = true;
        cell.by = by;
    }

    function cellGotDef(cell, def){
        assert(!cell.def);
        cell.def = def;
        loadCell(cell, parseDeps(def));
    }

    function checkCycle(cell, dep){
        while (cell) {
            if (cell === dep) {
                return true;
            }
            cell = cell.by;
        }
        return false;
    }

    function loadCell(cell, deps){
        var count = deps.length;
        for (var j=deps.length; j--;) {
            var dep = getCell(deps[j]);
            assert(!checkCycle(cell, dep), ['cell "', dep.path, '" has cycle dependencies.'].join(''));
            dep.reverse_deps.push(cell);
            cell.deps.push(dep);
            if (dep.defined) {
                count --;
            } else {
                if (!dep.requested) {
                    requestCell(dep, cell);
                }
            }
            deps[j] = dep;
        }
        cell.deps = deps;
        cell._undefined_deps_count = count;
        checkDefineCell(cell);
    }
    function getCurrentPath(){
        var path = getInteractiveScript().getAttribute('src');
        path = leftOfLast(path || '', '/');
        path = path ? path + '/' : '';
        return path;
    }

    var _base_path = getCurrentPath();
    var _paths = {};
    var _paths_regexp = null;
    function _rebuildPathsRegExp(){
        var array = [];
        for (var k in _paths) {
            if (hop(_paths, k)) {
                array.push(k);
            }
        }
        if (array.length) {
            sortBy(array, function (item){
                return item.length;
            }, true);
            _paths_regexp = new RegExp('^(?:' + array.join('|') + ')');
        }
    }

    function resolve(path){
        if (_paths_regexp) {
            var match;
            while ((match = _paths_regexp.exec(path))) {
                match = match[0];
                path = _paths[match] + path.slice(match.length);
            }
        }
        if (path.charAt(0) === '#') {
            path = path.slice(1);
        } else {
            path = _base_path + path;
        }
        return path;
    }

    function register(paths, base_path){
        for (var k in paths) {
            if (hop(paths, k)) {
                if (k === '/') {
                    _base_path = expand(paths[k], base_path);
                } else {
                    _paths[k] = '#' + expand(paths[k], base_path);
                }
            }
        }
        _rebuildPathsRegExp();
    }

    function require(path){
        return registry[path].exports;
    }

    var _waiting_def = null;
    function define(def){
        if (IE || isBeforeReady()) {
            var script = getInteractiveScript();
            var path = script.getAttribute('data-path');
            var cell = path ? getCell(path) : new Cell('');
            cellGotDef(cell, def);
        } else {
//            assert(!_waiting_def);
            _waiting_def = def;
        }
    }

    function async(fn){
        return '(' + fn + ')';
    }
    function defer(){

    }
    function await(){

    }

    function expand(path, base_path){
        if (path.slice(0, 2) === '~/') {
            return base_path + path.slice(2);
        }
        return path;
    }

    __celljs__ = function (paths, base_path){
        base_path = base_path || './';
        if (base_path.slice(0, 2) === './') {
            base_path = getCurrentPath() + base_path.slice(2);
        }
        register(paths, base_path);
    };
    __celljs__['_scriptLoad'] = onScriptLoad;
    __celljs__['_scriptRSChange'] = onScriptReadyStateChange;
    __celljs__['_scriptError'] = onScriptError;

    $exec = function (fn){
        cellGotDef(new Cell(''), fn);
    };
    $cell = function (p, callback){
        if (typeof p === 'string') {
            if (callback) {
                return $async(p)(callback);
            }
            return require(p);
        }
        return define(p);
    };
    $path = resolve;
    $async = function (p){
        if (typeof p === 'string') {
            return function (callback){
                var cell = new Cell('');
                cell.def = function (){
                    callback($cell(p));
                };
                loadCell(cell, [p]);
            };
        }
        return async(p);
    };
    $defer = defer;
    $await = await;
})();
